#include <stdio.h>
#include <modbus.h>
#include <pthread.h>
#include <string.h>
#include <semaphore.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <sys/msg.h>
#include <stdlib.h>
#include "cJSON.h"
#include "list.h"
#include "shmem.h" //共享内存
#include "msg_queue_peer.h"

//消息队列
struct msg
{
    long type;
    char buf[256];
};

char buf[128];      //车外环境
char buf1[128];     //车内环境
char buf2[128];     //车体状况
uint16_t dest[128]; //存放采集的数据
uint16_t dest1[128];
struct mb_node
{
    int key;        //唯一键值
    char name[128]; //数据点名称
    int type;       //数据点类型
    int addr;
};
struct mb_node_list
{
    struct mb_node node;
    struct list_head list;
};

//共享内存结构体
union val_t
{
    int b_val;   //bool类型存储空间
    int i_val;   //整形值存储空间
    float f_val; //浮点值存储空间
};
struct std_node
{
    int key;             //唯一键值
    int type;            //数据点类型
    int dev_type;        //数据点属于哪个设备，根据网关支持的设备自行定义
    union val_t old_val; //变化上报后需要更新旧值
    union val_t new_val; //从共享内存取出最新数据，放到new_val中
    int ret;             //默认为-1，采集成功后设置为0，采集失败再置-1
};

modbus_t *ctx;
struct list_head head;
struct mb_node_list *tmp;
struct list_head *pos;

void test(); //数据采集测试

void *handler_collect(void *arg)
{
    /*创建共享内存(与webserver共同建立共享内存)
    modbus读取传感器(modbus_slave)得到的数据，将数据写入共享内存中；webserver将数据从共享内存中读出；
    */
    struct std_node std;
    struct shm_param para;
    //初始化
    if (shm_init(&para, "shm", 128) < 0)
    {
        printf("shm init err\n");
        return NULL;
    }
    //获取共享内存地址
    int *p = (int *)shm_getaddr(&para);
    if (p == NULL)
    {
        printf("shm get addr err\n");
        return NULL;
    }
    printf("num=%d\n", *p);
    struct std_node *st = (struct std_node *)(p + 1);

    while (1)
    {
        sleep(2);
        list_for_each(pos, &head)
        {
            tmp = list_entry(pos, struct mb_node_list, list);
            if (tmp->node.key == 300)
            {
                modbus_read_registers(ctx, tmp->node.addr - 1 - 40000, 2, dest); //车外温度
            }
            else if (tmp->node.key == 301)
            {
                modbus_read_registers(ctx, tmp->node.addr - 1 - 40000, 2, dest + 2); //湿度
            }
            else if (tmp->node.key == 302)
            {
                modbus_read_registers(ctx, tmp->node.addr - 1 - 40000, 2, dest + 4); //光照强度
            }
            else if (tmp->node.key == 203)
            {
                modbus_read_bits(ctx, tmp->node.addr - 1, 1, dest + 13); //0x05 遮光板
            }
            else if (tmp->node.key == 204)
            {
                modbus_read_bits(ctx, tmp->node.addr - 1, 1, dest + 6); //加湿器
            }
            else if (tmp->node.key == 205)
            {
                modbus_read_bits(ctx, tmp->node.addr - 1, 1, dest + 7); //空调开关
            }
            else if (tmp->node.key == 305)
            {
                modbus_read_registers(ctx, tmp->node.addr - 1 - 40000, 1, dest + 8); //空调温控
            }
            else if (tmp->node.key == 303)
            {
                modbus_read_registers(ctx, tmp->node.addr - 1 - 40000, 2, dest1); //发动机温度
            }
            else if (tmp->node.key == 304)
            {
                modbus_read_registers(ctx, tmp->node.addr - 1 - 40000, 1, dest + 11); //水箱水量
            }
            else if (tmp->node.key == 206)
            {
                modbus_read_bits(ctx, tmp->node.addr - 1, 1, dest + 12); //防盗报警
            }
        }

        test(); //在Linux中打印读出的数据
        printf("---------共享内存点表数p=%d\n", *p);
        int i = 0;
        for (int i = 0; i < *p; i++)
        {
            if (st[i].key == 300)
            {
                st[i].old_val.f_val = st[i].new_val.f_val;
                st[i].new_val.f_val = modbus_get_float_dcba(&dest[0]);
                printf("车外温度=%.2f°C\n", st[i].new_val.f_val);
                if (st[i].new_val.f_val != 0)
                {
                    st[i].ret = 0;
                }
                else
                {
                    st[i].ret = -1;
                    printf("车外温度 err:=%d\n", st[i].ret);
                }
            }
            else if (st[i].key == 301)
            {
                st[i].old_val.f_val = st[i].new_val.f_val;
                st[i].new_val.f_val = modbus_get_float_dcba(&dest[2]);
                printf("车外湿度=%.2f%%\n", st[i].new_val.f_val);
                if (st[i].new_val.f_val != 0)
                {
                    st[i].ret = 0;
                }
                else
                {
                    st[i].ret = -1;
                    printf("车外湿度 err:=%d\n", st[i].ret);
                }
            }
            else if (st[i].key == 302)
            {
                st[i].old_val.i_val = st[i].new_val.i_val;
                st[i].new_val.i_val = dest[4];
                printf("光照强度=%d%%\n", st[i].new_val.i_val);
                if (st[i].new_val.i_val != 0)
                {
                    st[i].ret = 0;
                }
                else
                {
                    st[i].ret = -1;
                    printf("光照强度 err:=%d\n", st[i].ret);
                }
            }
            else if (st[i].key == 203)
            {
                st[i].old_val.b_val = st[i].new_val.b_val;
                st[i].new_val.b_val = dest[13];
                printf("遮光板=%d\n", st[i].new_val.b_val);
            }
            else if (st[i].key == 204)
            {
                st[i].old_val.b_val = st[i].new_val.b_val;
                st[i].new_val.b_val = dest[6];
                printf("加湿器=%d\n", st[i].new_val.b_val);
            }
            else if (st[i].key == 205)
            {
                st[i].old_val.b_val = st[i].new_val.b_val;
                st[i].new_val.b_val = dest[7];
                printf("空调开关=%d\n", st[i].new_val.b_val);
            }
            else if (st[i].key == 305)
            {
                st[i].old_val.i_val = st[i].new_val.i_val;
                st[i].new_val.i_val = dest[8];
                printf("空调温控=%d°C\n", st[i].new_val.i_val);
                if (st[i].new_val.i_val != 0)
                {
                    st[i].ret = 0;
                }
                else
                {
                    st[i].ret = -1;
                    printf("空调温控 err:=%d\n", st[i].ret);
                }
            }
            else if (st[i].key == 303)
            {
                st[i].old_val.f_val = st[i].new_val.f_val;
                st[i].new_val.f_val = modbus_get_float_dcba(&dest1[0]);
                printf("发动机温度=%.2f°C\n", st[i].new_val.f_val);
                if (st[i].new_val.f_val != 0)
                {
                    st[i].ret = 0;
                }
                else
                {
                    st[i].ret = -1;
                    printf("发动机温度 err:=%d\n", st[i].ret);
                }
            }
            else if (st[i].key == 304)
            {
                st[i].old_val.i_val = st[i].new_val.i_val;
                st[i].new_val.i_val = dest[11];
                printf("水箱水量=%d%%\n", st[i].new_val.i_val);
                if (st[i].new_val.i_val != 0)
                {
                    st[i].ret = 0;
                }
                else
                {
                    st[i].ret = -1;
                    printf("水箱水量 err:=%d\n", st[i].ret);
                }
            }
            else if (st[i].key == 206)
            {
                st[i].old_val.b_val = st[i].new_val.b_val;
                st[i].new_val.b_val = dest[12];
                printf("防盗报警=%d\n", st[i].new_val.b_val);
            }
        }
    }
    shm_del(&para);
    pthread_exit(NULL);
    return NULL;
}

void *handler_control(void *arg) //通过消息队列实现控制设备
{
    struct msg msg;
    while (1)
    {
        msg_queue_recv("msg", &msg, sizeof(msg), 32, 0);
        //反序列化
        // 先得到整个根节点
        cJSON *root = cJSON_Parse(msg.buf);
        if (root == NULL)
        {
            printf("JOSN格式错误\n");
            sleep(2);
            continue;
        }
        // 从根节点中获取名字叫key的子节点
        int key = cJSON_GetObjectItem(root, "key")->valueint;
        int val = cJSON_GetObjectItem(root, "val")->valueint;
        list_for_each(pos, &head)
        {
            tmp = list_entry(pos, struct mb_node_list, list);
            if (key == tmp->node.key)
            {
                if (key == 203)
                {
                    modbus_write_bit(ctx, tmp->node.addr - 1, val); //遮光板
                }
                else if (key == 204)
                {
                    modbus_write_bit(ctx, tmp->node.addr - 1, val); //加湿器
                }
                else if (key == 205)
                {
                    modbus_write_bit(ctx, tmp->node.addr - 1, val); //空调开关
                }
                else if (key == 206)
                {
                    modbus_write_bit(ctx, tmp->node.addr - 1, val); //防盗报警
                }
                else if (key == 305)
                {
                    modbus_write_registers(ctx, 5, tmp->node.addr - 1 - 40000, val); //空调温控
                }
            }
        }
    }
    pthread_exit(NULL);
    return NULL;
}

int main(int argc, char const *argv[])
{
    //(1)内核链表初始化
    INIT_LIST_HEAD(&head);

    //一、解析点表：1.确定需要哪个设备  2.确定采集还是控制
    //将node.json文件的数据读取出来
    FILE *fp = fopen("../config/config.json", "r");
    if (fp == NULL)
    {
        perror("fopen err");
        return -1;
    }
    char buf[4096] = "";
    fread(buf, 4096, 1, fp);
    //反序列化
    // 先得到整个根节点
    cJSON *root = cJSON_Parse(buf);
    // 从根节点中获取名字叫version的子节点
    cJSON *item = cJSON_GetObjectItem(root, "version");
    printf("%s:%s\n", item->string, item->valuestring);
    cJSON *report = cJSON_GetObjectItem(root, "report");
    item = cJSON_GetObjectItem(report, "type");
    printf("%s:%d\n", item->string, item->valueint);
    item = cJSON_GetObjectItem(report, "period");
    printf("%s:%d\n", item->string, item->valueint);

    //获取ip+端口号
    cJSON *dev = cJSON_GetObjectItem(root, "mb_dev");
    cJSON *addr = cJSON_GetObjectItem(dev, "addr");
    cJSON *por = cJSON_GetObjectItem(dev, "port");
    char ip[32] = "";
    int port = por->valueint;
    strcpy(ip, addr->valuestring);
    printf("ip=%s,port=%d\n", ip, port);

    cJSON *modbus = cJSON_GetObjectItem(root, "modbus");
    cJSON *data = cJSON_GetObjectItem(modbus, "data");

    for (int i = 0; i < cJSON_GetArraySize(data); i++)
    {
        cJSON *arr = cJSON_GetArrayItem(data, i);
        struct mb_node_list *array = (struct mb_node_list *)malloc(sizeof(struct mb_node_list));
        cJSON *key = cJSON_GetObjectItem(arr, "key");
        cJSON *name = cJSON_GetObjectItem(arr, "name");
        cJSON *addr = cJSON_GetObjectItem(arr, "addr");
        cJSON *type = cJSON_GetObjectItem(arr, "type");
        array->node.key = key->valueint;
        array->node.addr = addr->valueint;
        list_add(&array->list, &head); //添加到内核链表中去
    }

    // 测试放入循环链表情况 //4.遍历循环
    struct list_head *pos;
    struct mb_node_list *tmp;
    list_for_each(pos, &head)
    {
        tmp = list_entry(pos, struct mb_node_list, list);
        printf("key=%d  addr=%d \n", tmp->node.key, tmp->node.addr);
    }

    //1.创建实例
    ctx = modbus_new_tcp(ip, port);
    if (ctx == NULL)
    {
        perror("modbus_new_tcp err");
        return -1;
    }
    //2.设置从机id
    if (modbus_set_slave(ctx, 1) < 0)
    {
        perror("modbus_set_slave err");
        return -1;
    }
    //3.建立连接
    if (modbus_connect(ctx) < 0)
    {
        perror("mod_connect err");
        return -1;
    }
    //4.创建线程
    pthread_t tid;
    if (pthread_create(&tid, NULL, handler_collect, ctx) != 0) //线程1 采集温湿度
    {
        perror("创建采集线程失败");
        return -1;
    }
    pthread_t tid2;
    if (pthread_create(&tid2, NULL, handler_control, ctx) != 0) //线程2 控制蜂鸣器和灯
    {
        perror("创建控制线程失败");
        return -1;
    }
    pthread_join(tid, NULL);
    pthread_join(tid2, NULL);
    modbus_free(ctx);
    modbus_close(ctx);
    return 0;
}

//数据采集测试
void test()
{
    printf("------车外环境监测------\n");
    sprintf(buf, "车外温度:%.2f℃\n车外湿度:%.2f%%\n光照强度:%d%%\n", modbus_get_float_dcba(&dest[0]), modbus_get_float_dcba(&dest[2]), dest[4]);
    printf("%s", buf);
    printf("------车内环境监测------\n");
    sprintf(buf1, "遮光板:%d\n加湿器:%d\n空调开关:%d\n空调温控:%d°C\n", dest[14], dest[6], dest[7], dest[8]);
    printf("%s", buf1);
    printf("------车体状况监测------\n");
    sprintf(buf2, "发动机温度:%.2f°C\n水箱水量:%d%%\n防盗报警:%d\n", modbus_get_float_dcba(&dest1[0]), dest[11], dest[12]);
    printf("%s", buf2);
}